package elastic.service;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.css.sword.knowing.search.*;
import com.css.sword.knowing.search.aggregation.AggBucket;
import com.css.sword.knowing.search.aggregation.AggInput;
import com.css.sword.knowing.search.aggregation.SearchAndAggResult;
import com.css.sword.knowing.search.comm.SearchConst;
import com.css.sword.knowing.search.exception.ArgException;
import elastic.log.SearchLog;
import elastic.util.QueryBuilder;
import elastic.util.SearchErrorHandler;
import elastic.util.SearchFileUtil;
import elastic.util.SearchQueryMap;
import opennlp.tools.util.StringUtil;
import org.apache.commons.lang.StringUtils;
import org.json.JSONException;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author quxb
 * @version 1.0
 * @project 深圳OA
 * @package zhbg.oaxt.elastic
 * @file SearchVo.java 创建时间:2018年7月23日下午3:51:07
 * @title 标题（要求能简洁地表达出类的功能和职责）
 * @description 描述（简要描述类的职责、实现方式、使用注意事项等）
 * @copyright Copyright (c) 2018 中国软件与技术服务股份有限公司
 * @company 中国软件与技术服务股份有限公司
 * @module 模块: 模块名称
 * @reviewer 审核人
 * @history 修订历史（历次修订内容、修订人、修订时间等）
 */
@Service
public class SearchService {

    /**
     * 日志
     */
    private Logger log = LoggerFactory.getLogger(SearchService.class);

    /**
     * searchErrorHandler 注入
     */
    @Autowired
    private SearchErrorHandler searchErrorHandler;

    private final static String type_name = "all";
    private final static String current = "_current";
    private final static String stale = "_stale";
    private final static String history = "_history";



    public static void init() {
        Searcher.getTransportClient();
    }
    
    /**
     * 批量保存
     * @param index     索引名称
     * @param id        id值，用‘,’分割
     * @param value     待保存的值，json字符串，类型为json数组
     * @param flag      是否为更新插入，true：更新插入操作，false：插入操作
     * @param fullText  是否是全文更新操作，true：全文更新，false：业务更新
     * @return          错误信息，正确为空值
     * @throws IOException
     * @throws ArgException
     */
    private String bulkSave(String index, String id, String value, boolean flag, boolean fullText) throws IOException, ArgException
    {
        String msg = "";
        List<IndexDocument> data = new ArrayList<IndexDocument>();
        JSONArray jsonArray = (JSONArray)JSONArray.parse(value);
                
        String[] idArr = id.split(",");
        
        int size = idArr.length>jsonArray.size() ? jsonArray.size() : idArr.length;
        
        for(int i=0; i<size; i++)
        {
            JSONObject json = jsonArray.getJSONObject(i);
            if(fullText && !SearchQueryMap.isEmpty(json))
            {
                final SearchFileUtil searchFileUtil = new SearchFileUtil();
                json = searchFileUtil.AttachExtract(json.toString());
            }

            String key = idArr[i];
            IndexDocument doc = new IndexDocument();
            doc.setData(json);
            doc.setKey(key);
            
            data.add(doc);
        }
        
        IndexInput ii = new IndexInput();
        ii.setIndexName(index+current);
        ii.setTypeName(type_name);
        ii.setIndexDocs(data);
        
        if(flag)
            msg = Searcher.upsertIndex(ii);
        else
            msg = Searcher.createIndex(ii);
        
        if(flag && StringUtils.isEmpty(msg))
            bulkDeleteOld(index, id);
        
        return msg;
    }

    /**
     * 批量更新操作
     * @param index     索引名称
     * @param id        id值，用‘,’分割
     * @param value     待保存的值，json字符串，类型为json数组
     * @param fullText 是否是全文更新操作，true：全文更新，false：业务更新
     * @return          错误信息，正确为空值
     */
    private String bulkUpdate(String index, String id, String value, boolean fullText)
    {
        String msg = "";
        List<IndexDocument> data = new ArrayList<IndexDocument>();
        JSONArray jsonArray = (JSONArray)JSONArray.parse(value);
                
        String[] idArr = id.split(",");
                
        int size = idArr.length>jsonArray.size() ? jsonArray.size() : idArr.length;
        
        for(int i=0; i<size; i++)
        {
            JSONObject json = jsonArray.getJSONObject(i);
            String key = idArr[i];
            
            if(fullText && !SearchQueryMap.isEmpty(json))
            {
                final SearchFileUtil searchFileUtil = new SearchFileUtil();
                json = searchFileUtil.AttachExtract(json.toString());
            }

            String msg0 = Searcher.updateIndex(index+current, type_name, key, json.toJSONString());
            if(!StringUtils.isEmpty(msg0))
                msg0 = Searcher.updateIndex(index+stale, type_name, key, json.toJSONString());
            if(!StringUtils.isEmpty(msg0))
                msg0 = Searcher.updateIndex(index+history, type_name, key, json.toJSONString());
        
            if(!StringUtils.isEmpty(msg0))
                msg += "\n" + msg0;
        }
        
        return msg;
    }
    
    /**
     * 批量删除stale和 history索引库中的值
     * @param index     索引名称
     * @param ids        id值，用‘,’分割
     * @return
     */
    private String bulkDeleteOld(String index, String ids)
    {
        String ret = "";
        String[] arr = ids.split(",");
        List<String> idList = new ArrayList<String>();
        for(String str : arr)
        {
            if(!StringUtil.isEmpty(str))
                idList.add(str);
        }
        DeleteInput di = new DeleteInput();
        di.setIds(idList);

        di.setType(type_name);
        
        di.setIndexName(index+stale);
        ret = Searcher.deleteIndex(di);

        if(!StringUtils.isEmpty(ret))
        {
            if(ret.indexOf("errMsg") < 0)
                di.setIds(ret.split(","));
            
            di.setIndexName(index+history);
            ret = Searcher.deleteIndex(di);
        }
   
        return ret;
    }
    
    /**
     * 批量删除所有索引库中的值
     * @param index     索引名称
     * @param ids        id值，用‘,’分割
     * @return
     */
    private String bulkDeleteAll(String index, String ids)
    {
        String ret = "";
        String[] arr = ids.split(",");
        List<String> idList = new ArrayList<String>();
        for(String str : arr)
        {
            if(!StringUtil.isEmpty(str))
                idList.add(str);
        }
        DeleteInput di = new DeleteInput();
        di.setIds(idList);
        
        di.setIndexName(index+current);
        di.setType(type_name);
        ret = Searcher.deleteIndex(di);
        if(!StringUtils.isEmpty(ret))
        {
            if(ret.indexOf("errMsg") < 0)
                di.setIds(ret.split(","));
            
            di.setIndexName(index+stale);
            ret = Searcher.deleteIndex(di);
        } 
        if(!StringUtils.isEmpty(ret))
        {
            if(ret.indexOf("errMsg") < 0)
                di.setIds(ret.split(","));
            
            di.setIndexName(index+history);
            ret = Searcher.deleteIndex(di);
        }
        
        return ret;
    }

    /**
     * 添加或修改，待记录存在则修改，不存在直接添加
     *
     * @param index  索引名称
     * @param id     主键id  
     * @param value  待保存的记录，json字符串
     * @param fullText 是否是全文更新操作，true：全文更新，false：业务更新
     * @return string 保存结果，json字符串
     */
    public String saveOrUpdateSearch(String index, String id, String value, boolean fullText,String sessionId,int retryNo) {
        final StringBuffer stringBuffer = new StringBuffer("{\"code\":");
        String funcName = fullText?"fullTextSaveOrUpdate":"saveOrUpdate";

        String msg = "";
        if (id.indexOf(',') < 0) {
            if(fullText && !SearchQueryMap.isEmpty(value))
            {
                final SearchFileUtil searchFileUtil = new SearchFileUtil();
                value = searchFileUtil.AttachExtract(value).toJSONString();
                System.out.println("@@@@@@@@@@ " + value);
            }
            
            msg = Searcher.upsertIndex(index + current, type_name, id, value);
            if (StringUtils.isEmpty(msg)) {
                if (Searcher.docExist(index + stale, type_name, id))
                    Searcher.deleteIndex(index + stale, type_name, id);
                else if (Searcher.docExist(index + history, type_name, id))
                    Searcher.deleteIndex(index + history, type_name, id);
            }
        } else {
            try {
                msg = bulkSave(index, id, value, true, fullText);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                msg += "\n" + e.getMessage();
            }
        }
        if (StringUtils.isEmpty(msg)) {
            stringBuffer.append("0, \"message\":\"success\"}");
            SearchLog.postLog(funcName, index, id, stringBuffer.toString(), null, retryNo, "soa", "response", false,sessionId);
        } else {
            //重试2次
            if(retryNo>=0&&retryNo < 2){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                return saveOrUpdateSearch(index,id,value,fullText,sessionId,retryNo+1);
            }else{
                log.info("======更新添加索引出错====== " + msg);
                stringBuffer.append("01, \"message\":\"====更新添加索引出错====").append(msg).append("\"}");
                SearchLog.postLog(funcName, index, id, value, stringBuffer.toString(), retryNo, "soa", "error", false,sessionId);
            }
        }
        return stringBuffer.toString();
    }

    /**
     * 添加到es记录
     *
     * @param index  索引名称
     * @param id     主键id
     * @param value  添加的记录，json字符串
     * @param fullText 是否是全文更新操作，true：全文更新，false：业务更新
     * @return string 返回添加的结果，json字符串
     */
    public String addSearch(String index, String id, String value, boolean fullText,String sessionId,int retryNo) {
        final StringBuffer stringBuffer = new StringBuffer("{\"code\":");
        String funcName = fullText?"fullTextAdd":"add";
        String msg = "";
        // 添加
        if (id.indexOf(',') < 0)
        {   
            if(fullText && !SearchQueryMap.isEmpty(value))
            {
                final SearchFileUtil searchFileUtil = new SearchFileUtil();
                value = searchFileUtil.AttachExtract(value).toJSONString();
            }
            
            msg = Searcher.createIndex(index+current, type_name, id, value);
        }
        else
        {
            try {
                msg = bulkSave(index, id, value, false, fullText);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                msg += e.getMessage();
            }
        }

        if(StringUtils.isEmpty(msg))
        {
            stringBuffer.append("0, \"message\":\"success\"}");
            SearchLog.postLog(funcName, index, id, stringBuffer.toString(), null, retryNo, "soa", "response", false,sessionId);
//            if(Searcher.docExist(index+stale, type_name, id))
//                Searcher.deleteIndex(index+stale, type_name, id);
//            else if(Searcher.docExist(index+history, type_name, id))
//                Searcher.deleteIndex(index+history, type_name, id);
        }
        else
        {
            //重试2次
            if(retryNo>=0&&retryNo < 2){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                return addSearch(index,id,value,fullText,sessionId,retryNo+1);
            }
            log.info("=======添加索引出错====== " + msg);
            stringBuffer.append("02, \"message\":\"====添加检索出错====").append(msg).append("\"}");
            SearchLog.postLog(funcName, index, id, value, stringBuffer.toString(), retryNo, "soa", "error", false,sessionId);
        }

        return stringBuffer.toString();
    }

    /**
     * 修改es数据
     *
     * @param index 索引名称
     * @param id    主键id
     * @param value 更新的记录，json字符串
     * @param fullText 是否是全文更新操作，true：全文更新，false：业务更新
     * @return string 更新结果，json字符串
     */
    public String updateSearch(String index, String id, String value, boolean fullText,String sessionId,int retryNo) {
        final StringBuffer stringBuffer = new StringBuffer("{\"code\":");
        String funcName = fullText?"fullTextUpdate":"update";
        boolean flag = false;
        String msg = "";
        
        if(id.indexOf(',') < 0)
        {
            if(fullText && !SearchQueryMap.isEmpty(value))
            {
                final SearchFileUtil searchFileUtil = new SearchFileUtil();
                value = searchFileUtil.AttachExtract(value).toJSONString();
            }
            
            msg = Searcher.updateIndex(index+current, type_name, id, value);
            if(!StringUtils.isEmpty(msg))
                msg = Searcher.updateIndex(index+stale, type_name, id, value);
            if(!StringUtils.isEmpty(msg))
                msg = Searcher.updateIndex(index+history, type_name, id, value);
        }
        else
        {
            msg = bulkUpdate(index, id, value, fullText);
        }
        
        if(StringUtils.isEmpty(msg))
        {
            stringBuffer.append("0, \"message\":\"success\"}");
            SearchLog.postLog(funcName, index, id, stringBuffer.toString(), null, retryNo, "soa", "response", false,sessionId);
        }
        else
        {
            //重试2次
            if(retryNo>=0&&retryNo < 2){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                return updateSearch(index,id,value,fullText,sessionId,retryNo+1);
            }
            log.info("=====更新索引出错====== " + msg);
            stringBuffer.append("03, \"message\":\"====更新索引出错====").append(msg).append("\"}");
            SearchLog.postLog(funcName, index, id, value, stringBuffer.toString(), retryNo, "soa", "error", false,sessionId);
        }

        return stringBuffer.toString();
    }

    /**
     * 删除es数据
     *
     * @param index 索引名称
     * @param id    记录主键
     * @return string 返回json字符串
     */
    public String deleteSearch(String index, String id,String sessionId,int retryNo) {
        final StringBuffer stringBuffer = new StringBuffer("{\"code\":");
        final String funcName = "delete";

        String ret = "";
        if(id.indexOf(',') < 0)
        {
            ret = Searcher.deleteIndex(index+current, type_name, id);
            if(!ret.isEmpty())
                ret = Searcher.deleteIndex(index+stale, type_name, id);
            else if(!ret.isEmpty())
                ret = Searcher.deleteIndex(index+history, type_name, id);
        }
        else
        {
            ret = bulkDeleteAll(index, id);
        }
        
        if(StringUtils.isEmpty(ret))
        {
            stringBuffer.append("0, \"message\":\"success\"}");
            SearchLog.postLog(funcName, index, id, stringBuffer.toString(), null, retryNo, "soa", "response", false,sessionId);
        }
        else
        {
            if(ret.indexOf("errMsg:") < 0)
            {
                stringBuffer.append("0, \"message\":\"" + ret + "\"}");
                SearchLog.postLog(funcName, index, id, stringBuffer.toString(), null, retryNo, "soa", "response", false,sessionId);
            }
            else
            {
                //重试2次
                if(retryNo>=0&&retryNo < 2){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    return deleteSearch(index,id,sessionId,retryNo+1);
                }
                log.info("=====删除检索数据出错===== " + ret);
                stringBuffer.append("04, \"message\":\"====删除检索数据出错====").append(ret).append("\"}");
                SearchLog.postLog(funcName, index, id, null, stringBuffer.toString(), retryNo, "soa", "error", false,sessionId);
            }
        }
            
        return stringBuffer.toString();
    }

    /**
     * 检索
     *
     * @param index      索引名称
     * @param fields     检索字段
     * @param query      检索条件，json格式
     * @param orderField 排序字段
     * @param order      升序/降序
     * @param from       分页/起始
     * @param size       分页/条数
     * @param highLights 高亮字段，多个字段用“,”分割
     * @param preTag     高亮标签
     * @param postTag    高亮标签
     * 
     * @return 检索结果
     */
    public String search(String index, String query, String fields, List<String> orderFieldList, int from, int size, String highLights, String preTag, String postTag) {
        final StringBuffer stringBuffer = new StringBuffer("{\"code\":");

        SearchInput si = new SearchInput();
        si.setIndexName(index);
        si.setTypeNames(new String[]{type_name});

        if(!StringUtils.isEmpty(query))
        {
            List<SearchField> where = QueryBuilder.create(query);
            if(null != where)
                si.setSearchFields(where);
        }
        
        if (null!=fields && !StringUtils.isEmpty(fields)) {
              si.setReturnFields(fields.split(","));
        }

        if(!StringUtils.isEmpty(highLights))
        {
            String[] fieldArr = highLights.split(",");
            HighLightField[] hlfs = new HighLightField[fieldArr.length]; 
            for(int i=0; i<fieldArr.length; i++)
            {
                HighLightField hlf = new HighLightField();
                hlf.setFieldName(fieldArr[i]);
                hlf.setFragmentSize(25);
                if(!StringUtils.isEmpty(preTag) && !StringUtils.isEmpty(postTag))
                {
                    hlf.setHighlighterPreTags(preTag); 
                    hlf.setHighlighterPostTags(postTag);
                }
                hlfs[i] = hlf;
            }
            si.setHighLightFields(hlfs);
        }
        
        if (null!=orderFieldList && orderFieldList.size()>0) {
            
            List<OrderField> orderList = new ArrayList<OrderField>();
            for(String field : orderFieldList)
            {
                String[] arr = field.split("#");

                OrderField sort = new OrderField();
                
                sort.setFieldName(arr[0]);
                if(arr.length > 1)
                {
                    sort.setOrder("asc".equalsIgnoreCase(arr[1]) ? SearchConst.SEARCH_ORDER_TYPE_ASC : SearchConst.SEARCH_ORDER_TYPE_DESC);
                }
                else
                {
                    sort.setOrder(SearchConst.SEARCH_ORDER_TYPE_DESC);
                }
                orderList.add(sort);
            }
            si.setOrderFields(orderList);
        }
                         
        if(from < 1)
            from = 1;
        
        if(size < 0)
            size = 10;
        
        si.setFrom((from - 1) * size);
        si.setSize(size);
 
        List<SearchInput> sis = new ArrayList<SearchInput>();
        sis.add(si);

        List<SearchResult> rss;
        try {
            rss = Searcher.multiIndexSearch(sis);
        
            if(StringUtils.isEmpty(rss.get(0).getErrMsg()))
            {
                if(null == rss.get(0))
                    stringBuffer.append("0, \"message\":\"success\",\"data\":").append("{}}");
                else
                    stringBuffer.append("0, \"message\":\"success\",\"data\":").append(rss.get(0).toJson()).append("}");
            }
            else
            {
                stringBuffer.append("05, \"message\":\"====检索出错====\n").append(rss.get(0).getErrMsg()).append("\"}");
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
             log.info("====检索异常错误====\n" + e);
             stringBuffer.append("05, \"message\":\"====检索异常错误====\n").append(e).append("\"}");
        }

        return stringBuffer.toString();
    }

    private AggBucket setFacetField(String fname, int size)
    {
        AggBucket ab = new AggBucket();
        
        ab.setAggName(fname);
        ab.setField(fname);
        ab.setSize(size);
        ab.setType(SearchConst.AGG_TYPE_TERMS);
        
        return ab;
    }
    
    /**
     * 统计待办类型
     *
     * @param index          索引名称
     * @param query          检索条件，json格式
     * @param aggregate_word 分组字段
     * @return 统计结果
     */
    public String aggregate(String index, String query, String aggregate_word) {
        final StringBuffer stringBuffer = new StringBuffer("{\"code\":");

        SearchInput si = new SearchInput();

        AggInput ai = new AggInput();
        List<AggBucket> abs = new ArrayList();
        ai.setAggBuckets(abs);
        ai.setIndexName(index);
        ai.setTypeNames(new String[] { type_name });

        ai.setSi(si);

        List<SearchField> sfs = QueryBuilder.create(query);
        if(null != sfs)
            si.setSearchFields(sfs);

        // si.setReturnFields(new String[]{"hkey", "title", "urlname",
        // "sitename", "pagetime", "content"});

        si.setSize(0);
        abs.add(setFacetField(aggregate_word, 100));

        try {
            SearchAndAggResult saar = Searcher.makeAggregation(ai);
                    
            stringBuffer.append("0, \"message\":\"success\", \"data\":").append(saar.getAggResult().toJson()).append("}");
        } catch (Exception e) {
            log.info("====检索统计，oneDimCount出错=====" + e);
            stringBuffer.append("06, \"message\":\"====检索统计，oneDimCount出错====").append(e).append("\"}");
        }

        return stringBuffer.toString();
    }

    public String move() throws JSONException {
        /*
        final IPSearchClient ipSearchClient = IPSearch.getClient().getIPSearchClient();

        String searchResult = ipSearchClient.search("db_current_v1", "all", "", new SortFieldList(), 0, 1000);
        Map map = JSON.parseObject(searchResult);
        JSONArray jsonArray = (JSONArray)map.get("hits");
        for (Object jsonObject : jsonArray) {
            Map object = (Map)jsonObject;
            Object identity1 = object.get("identity");
            Object oracleId1 = object.get("oracleId");
            Object todoType1 = object.get("todoType");
            String identity = (null != identity1) ? identity1.toString() : "";
            String oracleId = (null != oracleId1) ? oracleId1.toString() : "";
            String todoType = (null != todoType1) ? todoType1.toString() : "";

            String id = oracleId + "-" + identity + "-" + todoType;
            String json = JSON.toJSONString(object);
            ipSearchClient.index("db_current", "all", id, json);
        }
*/
        return "";
    }

}
